/* 
  Copyright (c) SilverLining 2015
  Date:    2016-1-7
  File:    Game.cpp  
  Description: 

*/

#include "stdafx.h"

using namespace System;
using namespace System::Security;
using namespace System::Threading;
using namespace System::Collections::Generic;
using namespace System::Runtime::Serialization;

#include "shim_object.h"
#include "shim_process.h"
#include "BadResultException.h"
#include "Package.h"
#include "Archiver.h"
#include "GameObject.h"
#include "TimerState.h"
#include "Package.h"
#include "MessageType.h"
#include "Game.h"
#include "Message.h"
#include "Process.h"

using namespace m;
using namespace m::alloc;
using namespace m::io;

namespace SL
{
	namespace M
	{
		Game::Game()
			: _ProcessManager(new process_manager()),
			_PackageManager(new package_manager()),
			_FileStreamer(new file_streamer()),
			_PoolCleaner(new pool_cleaner()),
			_processLock(gcnew Object()),
			_processes(gcnew List<Process^>())
		{

		}

		Game::~Game()
		{
			Game::!Game();
		}

		Game::!Game()
		{
			if (_ProcessManager)
			{
				_ProcessManager->release();
			}
			if (_PackageManager)
			{
				_PackageManager->release();
			}
			if (_FileStreamer)
			{
				_FileStreamer->release();
			}
			if (_PoolCleaner)
			{
				_PoolCleaner->release();
			}
		}

		void Game::AddProcess(Process ^ process)
		{
			if (process == nullptr)
			{
				throw gcnew ArgumentNullException("process");
			}

			if (process->Game != nullptr)
			{ 
				throw gcnew ArgumentException("Process is already started.");
			}

			Monitor::Enter(_processLock);
			try
			{
				process->Game = this;
				_ProcessManager->add_process(process->_Process);
				_processes->Add(process);
			}
			catch (bad_result_exception& bre)
			{
				throw gcnew BadResultException(&bre);
			}
			finally
			{
				Monitor::Exit(_processLock);
			}
		}

		bool Game::RemoveProcess(Process ^ process)
		{
			if (process == nullptr)
			{
				throw gcnew ArgumentNullException("process");
			}

			if (process->Game != this)
			{
				throw gcnew ArgumentException("Process is not started or not owned by this game object.");
			}

			Monitor::Enter(_processLock);
			try
			{
				if (_processes->Remove(process))
				{
					_ProcessManager->remove_process(process->_Process);
					process->Game = nullptr;
					return true;
				}
				return false;
			}
			catch (bad_result_exception& bre)
			{
				throw gcnew BadResultException(&bre);
			}
			finally
			{
				Monitor::Exit(_processLock);
			}
		}

		void Game::Start()
		{
			Monitor::Enter(_processLock);
			try
			{
				_ProcessManager->start();
				_ProcessManager->add_process(_PackageManager);
				_ProcessManager->add_process(_FileStreamer);
				_ProcessManager->add_process(_PoolCleaner);
			}
			catch (bad_result_exception& bre)
			{
				throw gcnew BadResultException(&bre);
			}
			finally
			{
				Monitor::Exit(_processLock);
			}
		}

		void Game::Stop()
		{
			Monitor::Enter(_processLock);
			try
			{
				_ProcessManager->stop();
				_processes->Clear();
			}
			catch (bad_result_exception& bre)
			{
				throw gcnew BadResultException(&bre);
			}
			finally
			{
				Monitor::Exit(_processLock);
			}
		}

		void Game::Pause()
		{
			try
			{
				_ProcessManager->pause();
			}
			catch (bad_result_exception& bre)
			{
				throw gcnew BadResultException(&bre);
			}
		}

		void Game::Resume()
		{
			try
			{
				_ProcessManager->resume();
			}
			catch (bad_result_exception& bre)
			{
				throw gcnew BadResultException(&bre);
			}
		}

		void Game::Flush()
		{
			_ProcessManager->flush();
		}

		ICollection<Process^>^ Game::Processes::get()
		{
			Monitor::Enter(_processLock);
			try
			{
				return gcnew List<Process^>(_processes);
			}
			finally
			{
				Monitor::Exit(_processLock);
			}
		}

		bool Game::Started::get()
		{
			return _ProcessManager->started();
		}

		bool Game::Resumed::get()
		{
			return _ProcessManager->resumed();
		}
	}
}